package cn.cxy.sidebar

import android.content.Context
import android.graphics.Canvas
import android.graphics.Paint
import android.util.AttributeSet
import android.view.MotionEvent
import androidx.appcompat.widget.AppCompatTextView
import kotlin.math.abs
import kotlin.math.floor
import kotlin.math.max
import kotlin.math.sin

class WaveSideBar : AppCompatTextView {
    private var letters = arrayOf(
        "#", "A", "B", "C", "D", "E", "F", "G", "H", "I",
        "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V",
        "W", "X", "Y", "Z"
    )
    private lateinit var textPaint: Paint
    private lateinit var bigTextPaint: Paint
    private var callBack: SideBarSelectCallBack? = null
    private var eventY = 0f
    private var w = 0f
    private var sideTextWidth = 0f
    private var lastSelectIndex = -1

    /**
     * 是否重新测量宽高
     */
    private var isTouching = false
    private var itemHeight = 0f

    /**
     * 振幅
     */
    private var A = dp(100).toFloat()

    /**
     * 波峰与bigText之间的距离
     */
    private var gapBetweenText = dp(50)

    /**
     * 开口数量
     */
    private var openCount = 13

    /**
     * 字体缩放，基于textSize
     */
    private var fontScale = 1f
    private var bigTextSize = 0f

    constructor(context: Context?) : super(context) {
        init(null)
    }

    constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) {
        init(attrs)
    }

    constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(
        context,
        attrs,
        defStyleAttr
    ) {
        init(attrs)
    }

    fun setFontScale(fontScale: Float): WaveSideBar {
        this.fontScale = fontScale
        return this
    }

    fun setDataResource(data: Array<String>) {
        letters = data
        invalidate()
    }

    fun setOnSelectCallBack(callBack: SideBarSelectCallBack) {
        this.callBack = callBack
    }

    fun setBigTextSize(bigTextSize: Float): WaveSideBar {
        this.bigTextSize = bigTextSize
        bigTextPaint.textSize = bigTextSize
        //        invalidate();
        return this
    }

    fun setA(a: Float): WaveSideBar {
        A = a
        //        invalidate();
        return this
    }

    fun setGapBetweenText(gapBetweenText: Int): WaveSideBar {
        this.gapBetweenText = gapBetweenText
        //        invalidate();
        return this
    }

    fun setOpenCount(openCount: Int): WaveSideBar {
        this.openCount = openCount
        //        invalidate();
        return this
    }

    private fun calculateAW(height: Int) {
        itemHeight = height * 1.0f / letters.size
        /**
         * 开口宽度
         */
        val openedWidth = itemHeight * openCount
        //角速度 2PI/t 周期
        w = (Math.PI * 2.0f / (openedWidth * 2)).toFloat()
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        val mode = MeasureSpec.getMode(widthMeasureSpec)
        var viewWidth = MeasureSpec.getSize(widthMeasureSpec)
        calculateAW(MeasureSpec.getSize(heightMeasureSpec))
        if (mode == MeasureSpec.UNSPECIFIED || mode == MeasureSpec.AT_MOST) {
            viewWidth =
                if (!isTouching) (sideTextWidth + paddingLeft + paddingRight).toInt() else (A + gapBetweenText + getBigTextWidth() + paddingLeft + paddingRight).toInt()
        }
        setMeasuredDimension(viewWidth, MeasureSpec.getSize(heightMeasureSpec))
    }

    private fun init(attrs: AttributeSet?) {
        if (attrs != null) {
            val typedArray =
                context.obtainStyledAttributes(attrs, R.styleable.WaveSideBar)
            A = typedArray.getInteger(R.styleable.WaveSideBar_A, dp(100)).toFloat()
            fontScale = typedArray.getFloat(R.styleable.WaveSideBar_fontScale, 1f)
            bigTextSize = typedArray.getFloat(R.styleable.WaveSideBar_bigTextSize, textSize * 3)
            gapBetweenText = typedArray.getInteger(R.styleable.WaveSideBar_gapBetweenText, dp(50))
            openCount = typedArray.getInteger(R.styleable.WaveSideBar_openCount, 13)
        } else {
            bigTextSize = textSize * 3
        }
        textPaint = Paint(Paint.ANTI_ALIAS_FLAG)
        textPaint.color = currentTextColor
        textPaint.textSize = textSize
        textPaint.textAlign = Paint.Align.CENTER
        bigTextPaint = Paint(Paint.ANTI_ALIAS_FLAG)
        bigTextPaint.color = currentTextColor
        bigTextPaint.textSize = bigTextSize
        bigTextPaint.textAlign = Paint.Align.CENTER
        val sideTextHeight =
            textPaint.fontMetrics.descent - textPaint.fontMetrics.ascent
        sideTextWidth = textPaint.measureText("W")
    }


    override fun onTouchEvent(event: MotionEvent): Boolean {
        val startTouchX = (measuredWidth - A).toInt()
        when (event.action) {
            MotionEvent.ACTION_DOWN, MotionEvent.ACTION_MOVE -> {
                if (event.x > startTouchX) {
                    eventY = event.y
                    if (!isTouching) {
                        isTouching = true
                        requestLayout()
                    } else {
                        invalidate()
                    }
                } else {
                    if (isTouching) {
                        resetDefault()
                    }
                }
                return true
            }
            MotionEvent.ACTION_CANCEL, MotionEvent.ACTION_UP -> {
                resetDefault()
                return true
            }
        }
        return super.onTouchEvent(event)
    }

    private fun resetDefault() {
        isTouching = false
        eventY = 0f
        requestLayout()
    }

    override fun onDraw(canvas: Canvas) {
        val singleSideCount = openCount / 2
        val index =
            if (isTouching && eventY >= 0 && eventY <= measuredHeight)
                floor((eventY / itemHeight).toDouble()).toInt()
            else
                -(singleSideCount + 1)
        val sideX = sideTextWidth / 2 + paddingRight
        for (i in letters.indices) {
            textPaint.textSize = textSize
            val y = (itemHeight * (i + 1)).toInt()
            var x: Int
            if (abs(i - index) > singleSideCount) {
                x = (measuredWidth - sideX).toInt()
            } else {
                val percent = eventY / itemHeight
                val t = (i * itemHeight - eventY).toInt()
                var v = A * sin(w * t + Math.PI / 2)

                //如果算出来小于字体宽度 就取字体宽度
                v = max(v, sideX.toDouble())
                x = (measuredWidth - v).toInt()
                //根据delta缩放字体
                if (v == sideX.toDouble()) {
                    textPaint.textSize = textSize
                } else {
                    val delta = Math.abs(i - percent) / singleSideCount
                    val textSize =
                        textSize + (1 - delta) * textSize * fontScale
                    //                    textSize=Math.max(textSize,getTextSize());
                    textPaint.textSize = textSize
                }
            }
            canvas.drawText(letters[i], x.toFloat(), y.toFloat(), textPaint!!)
        }
        if (index != -(singleSideCount + 1)) {
            canvas.drawText(
                letters[index],
                paddingLeft + getBigTextWidth() / 2,
                (itemHeight * (index + 1)),
                bigTextPaint
            )
            //如果选择项没有改变，就不进行回调。
            if (index != lastSelectIndex) {
                callBack?.onSelect(index, letters[index])
            }
            lastSelectIndex = index
        }
    }

    private fun getBigTextWidth() = bigTextPaint.measureText("W")
    private fun dp(v: Int): Int {
        val scale = context.resources.displayMetrics.density
        return (v * scale + 0.5f).toInt()
    }
}

